﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;
using System.Windows.Forms;
using MinecraftProtocol.Types;

namespace MinecraftProtocolDefinitionBuilder
{
    class Program
    {
        
        static void Main(string[] args)
        {
            string basePath = Path.GetDirectoryName(Application.ExecutablePath);

            string[] linesTemp = File.ReadAllLines(basePath+"\\rawdump.txt");

            List<string> lines = new List<string>();
            foreach (string line in linesTemp)
            {
                string cleanLine = line.Trim();
                if (cleanLine.Length > 0)
                    lines.Add(cleanLine);
            }

            bool inPacketDef = false;

            List<McPacket> packets = new List<McPacket>();

            bool isFirstFieldRow = false;

            McPacket currentPacket = null;

            string currentName = "";
            byte currentId = 0;
            McPacketDirection currentDirection = McPacketDirection.TwoWay;

            int row = 0;
            foreach (string line in lines)
            {
                if (row == lines.Count - 1) break;

                if (!inPacketDef)
                {
                    if (line.StartsWith("Packet ID"))
                    {
                        inPacketDef = true;
                        isFirstFieldRow = true;
                    }
                    else
                    {
                        int bs = line.IndexOf("(");
                        int be = line.IndexOf(")");

                        if ((bs > -1) && (be > -1))
                        {
                            currentName = line.Substring(0, bs).Trim();
                            currentId = byte.Parse(line.Substring(bs + 1, be - bs - 1).Trim().Substring(2), System.Globalization.NumberStyles.HexNumber, null);
                        }
                        else
                        {
                            switch (line.ToLower().Trim())
                            {
                                case "client to server":
                                    currentDirection = McPacketDirection.ClientToServer;
                                    break;
                                case "server to client":
                                    currentDirection = McPacketDirection.ServerToClient;
                                    break;
                                case "two-way":
                                    currentDirection = McPacketDirection.TwoWay;
                                    break;
                                default:
                                    throw new Exception("invalid direction!");
                            }

                            //start a new packet
                            currentPacket = new McPacket();
                            currentPacket.PacketId = currentId;
                            currentPacket.PacketName = currentName;
                            currentPacket.PacketDirection = currentDirection;
                            currentPacket.Fields = new List<McPacketField>();
                            packets.Add(currentPacket);
                        }
                    }
                }
                else
                {
                    if (line.StartsWith("Total Size:"))
                    {
                        inPacketDef = false;
                        //currentPacketName = null;
                        //readyForNextPacket = true;
                    }
                    else
                    {
                        string[] cells = line.Split(new string[1] { "\t" }, StringSplitOptions.None);
                        if (((cells.Length < 2) && (!isFirstFieldRow)) || ((cells.Length < 3) && (isFirstFieldRow)))
                            throw new Exception("bad cells");

                        int fieldOfs = isFirstFieldRow ? 1 : 0;

                        McPacketField field = new McPacketField();
                        field.FieldName = cells[0 + fieldOfs].Trim();
                        field.FieldType = cleanType(cells[1 + fieldOfs]);
                        field.Example = cells.Length > 2 + fieldOfs ? cells[2 + fieldOfs].Trim() : "";

                        field.Notes = cells.Length > 3 + fieldOfs ? cells[3 + fieldOfs].Trim() : "";
                        currentPacket.Fields.Add(field);
                        isFirstFieldRow = false;

                    }
                }

                row++;
            }

            //sanity check data types
            Dictionary<string, int> fieldCounts = new Dictionary<string, int>();
            foreach (McPacket packet in packets)
            {
                foreach (McPacketField field in packet.Fields)
                {
                    if ((field.FieldType == null) || (field.FieldType == ""))
                        throw new Exception("invalid field type!");

                    if (!fieldCounts.ContainsKey(field.FieldType))
                        fieldCounts.Add(field.FieldType, 1);
                    else
                        fieldCounts[field.FieldType] = fieldCounts[field.FieldType] + 1;

                }
            }

            XmlSerializer serializer = new XmlSerializer(typeof(List<McPacket>));
            MemoryStream stream = new MemoryStream();
            serializer.Serialize(stream, packets);
            string xml = System.Text.Encoding.UTF8.GetString(stream.ToArray());

            string outPath = basePath + "\\packets.xml";
            if (File.Exists(outPath)) File.Delete(outPath);
            File.AppendAllText(outPath, xml);
        }

        static string cleanType(string type)
        {
            type = type.Trim().ToLower();
            type = type.Replace("unsigned ", "");
            type = type.Replace("boolean", "bool");
            return type;
        }
    }
}
